/**
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.guicerecipes.spring.converter;

import java.io.*;
import java.util.*;

import org.guicerecipes.spring.converter.ProduceMethod.*;

import com.google.common.base.*;

/** @version $Revision: 1.1 $ */
public class ModuleGenerator {
	private static final Joiner COMMA_JOINER = Joiner.on(", ");
	private final SpringConverter converter;
	private final PrintWriter writer;
	private final List<ProduceMethod> methods = new ArrayList<ProduceMethod>();

	public ModuleGenerator(SpringConverter converter, PrintWriter writer) {
		this.converter = converter;
		this.writer = writer;
	}

	public void generate() {
		start();
		for (ProduceMethod method : methods) {
			write(method);
		}
		end();
	}

	public ProduceMethod startProvides(String name, String className) {
		ProduceMethod method = new ProduceMethod(name, className);
		methods.add(method);
		return method;

	}

	protected void start() {
		println("//----------------------------------------------------------------------------------------");
		println("// Generated by the Guice SpringConverter at " + new Date());
		println("//----------------------------------------------------------------------------------------");

		if (converter.hasPackage()) {
			println("package %s;", converter.getPackageName());
			println();
		}
		println();
		SortedSet<String> names = converter.getImports();
		for (String name : names) {
			println("import %s;", name);
		}
		println();
		println("public class %s extends AbstractModule {", converter.getClassName());
	}

	protected void end() {
		println();
		println("}");
		println();
	}

	protected void write(ProduceMethod method) {
		String name = method.getName();
		String className = method.getClassName();
		println();
		println("  @Provides @Named(\"%s\")", name);
		println("  %s %s(%s) {", className, asJavaIdentifier(name), COMMA_JOINER.join(method.getParameters()));
		List<MethodCall> methodCalls = method.getMethodCalls();
		if (methodCalls.isEmpty()) {
			print("    return ");
			printConstructor(method);
			println();
		} else {
			print("    %s answer = ", className);
			printConstructor(method);
			println(";");
			for (MethodCall methodCall : methodCalls) {
				println("    %s", methodCall);
			}
			println("    return answer;");
		}
		println("  }");
	}

	/**
	 * Lets remove any dodgy characters from the method name
	 */
	public static String asJavaIdentifier(String name) {
		return name.replace('-', '_').replace('.', '_').replace('/', '_').replace('\\', '_');
	}

	protected void printConstructor(ProduceMethod method) {
		print("%s(%s)", method.getClassName(), COMMA_JOINER.join(method.getConstructorExpressions()));
	}

	protected void print(String text, Object... args) {
		writer.print(String.format(text, args));
	}

	protected void println(String text, Object... args) {
		writer.println(String.format(text, args));
	}

	protected void println(String text) {
		writer.println(text);
	}

	protected void println() {
		writer.println();
	}
}
